package xgenerator.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

/**
 * <p>
 * Title：JdbcContext
 * </p>
 * <p>
 * Description：
 * </p>
 * <p>
 * Copyright: Copyright (c) 2012
 * </p>
 * <p>
 * Company: liss
 * </p>
 * @author <a href="mailto:shushanlee@msn.com">liss</a>
 * @version 1.0
 * @see 
 * @since 1.0
 */
public class JdbcContext {
	
	/**
	 * JDBC_CONTEXT_MAP
	 */
	private static final Map<String, JdbcContext> JDBC_CONTEXT_MAP = Collections.synchronizedMap(new HashMap<String, JdbcContext>());
	
	/**
	 * 获取默认数据源[default]的JdbcContext
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return
	 */
	public static final JdbcContext getJdbcContext() {
		JdbcContext context = JDBC_CONTEXT_MAP.get(Configuration.DEFAULT_DS_NAME);
		if(null == context) {
			context = new JdbcContext();
			JDBC_CONTEXT_MAP.put(Configuration.DEFAULT_DS_NAME, context);
		}
		return context;
	}
	
	/**
	 * 获取指定数据源名[dataSourceName]数据源的JdbcContext
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param dataSourceName 数据源名称
	 * @return
	 */
	public static final JdbcContext getJdbcContext(String dataSourceName) {
		JdbcContext context = JDBC_CONTEXT_MAP.get(dataSourceName);
		if(null == context) {
			context = new JdbcContext(dataSourceName);
			JDBC_CONTEXT_MAP.put(dataSourceName, context);
		}
		return context;
	}
	
	/**
	 * 获取指定数据源名[dataSourceName]的[isContainerDataSource]数据源的JDBCContext
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param dataSourceName 数据源名称
	 * @param isContainerDataSource 是否为容器数据源
	 * @return
	 */
	public static final JdbcContext getJdbcContext(String dataSourceName, boolean isContainerDataSource) {
		JdbcContext context = null;
		if(isContainerDataSource) {
			dataSourceName = "jndi." + dataSourceName;
			context = JDBC_CONTEXT_MAP.get("jndi." + dataSourceName);  //避免与同名的非容器数据源混淆
			if(null == context) {
				context = new JdbcContext(dataSourceName, isContainerDataSource);
				JDBC_CONTEXT_MAP.put("jndi." + dataSourceName, context);
			}
		} else {
			context = JDBC_CONTEXT_MAP.get(dataSourceName);
			if(null == context) {
				context = new JdbcContext(dataSourceName, isContainerDataSource);
				JDBC_CONTEXT_MAP.put(dataSourceName, context);
			}
		}
		
		return context;
	}
	
	/**
	 * 关闭所有Connection
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 */
	public static void closeConnections() {
		Set<String> keySet = JDBC_CONTEXT_MAP.keySet();
		Iterator it = keySet.iterator();
		while(it.hasNext()) {
			JdbcContext context = JDBC_CONTEXT_MAP.get(it.next());
			context.closeConnection();
		}
	}
	
	/**
	 * connectionHolder
	 */
	private ThreadLocal<Connection> connectionHolder;

	/**
	 * constructor
	 */
	private JdbcContext() {
		this.connectionHolder = new ConnectionHolder<Connection>();
	}
	
	/**
	 * constructor
	 * @param dataSourceName 数据源名
	 */
	private JdbcContext(String dataSourceName) {
		this.connectionHolder = new ConnectionHolder<Connection>(dataSourceName);
	}
	
	/**
	 * constructor
	 * @param dataSourceName 数据源名
	 * @param isContainerDataSource 是否为容器数据源
	 */
	private JdbcContext(String dataSourceName, boolean isContainerDataSource) {
		this.connectionHolder = new ConnectionHolder<Connection>(dataSourceName, isContainerDataSource);
	}
	
	
	/**
	 * currentConnection
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return
	 */
	public Connection currentConnection() {
		return this.connectionHolder.get();
	}
	
	/**
	 * Close currentConnection
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 */
	public void closeConnection() {
		Connection conn = currentConnection();
		if(null != conn) {
			try {
				if(!conn.isClosed()) {
					conn.close();
				}
			} catch (SQLException ignore) {
			}
		}
	}

	/**
	 * Begin Transaction
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @return 
	 */
	public Transaction beginTransaction() {
		try {
			Connection conn = currentConnection();
			conn.setAutoCommit(false);
			return TransactionFactory.getTransaction(conn);
		} catch (SQLException e) {
			throw new RuntimeException(e);
		}
	}
	
	/**
	 * close Connection
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param stmt
	 */
	public static void close(Connection conn) {
		if(null != conn) {
			try {
				if(!conn.isClosed()) {
					conn.close();
				}
			} catch (SQLException ignore) {
			}
		}
	}
	
	/**
	 * close Statement
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param stmt
	 */
	public static void close(Statement stmt) {
		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException ignore) {
			}
		}
	}

	/**
	 * close ResultSet
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param rs
	 */
	public static void close(ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException ignore) {
			}
		}
	}
	
	/**
	 * close(Connection conn, Statement stmt, ResultSet rs)
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param conn
	 * @param stmt
	 * @param rs
	 */
	public static void close(Connection conn, Statement stmt, ResultSet rs) {
		close(rs);
		close(stmt);
		close(conn);
	}
	
	/**
	 * close(Statement stmt, ResultSet rs)
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param stmt
	 * @param rs
	 */
	public static void close(Statement stmt, ResultSet rs) {
		close(rs);
		close(stmt);
	}
	
	/**
	 * <p>
	 * Title：ConnectionHolder
	 * </p>
	 * <p>
	 * Copyright: Copyright (c) 2012
	 * </p>
	 * <p>
	 * Company: liss
	 * </p>
	 * @author <a href="mailto:shushanlee@msn.com">liss</a>
	 * @param <T>
	 * @version 1.0
	 * @see 
	 * @since 1.0
	 */
	private class ConnectionHolder<T extends Connection> extends ThreadLocal<Connection> {
		
		/**
		 * DataSource
		 */
		private DataSource dataSource;
		
		/**
		 * constructor
		 */
		public ConnectionHolder() {
			this.dataSource = new Configuration().configure().buildDataSourceFactory().getDefaultDataSource();
		}
		
		/**
		 * constructor
		 * @param dataSourceName
		 */
		public ConnectionHolder(String dataSourceName) {
			this.dataSource = new Configuration().configure().buildDataSourceFactory().getDataSource(dataSourceName);
		}
		
		/**
		 * constructor
		 * @param dataSourceName
		 */
		public ConnectionHolder(String dataSourceName, boolean isContainerDataSource) {
			this.dataSource = new Configuration().configure().buildDataSourceFactory(isContainerDataSource).getDataSource(dataSourceName);
		}
		
		/**
		 * constructor
		 * @param dataSource
		 */
		public ConnectionHolder(DataSource dataSource) {
			this.dataSource = dataSource;
		}
		
		/**
		 * initialValue
		 * @author <a href="mailto:shushanlee@msn.com">liss</a>
		 * @return
		 * @see java.lang.ThreadLocal#initialValue()
		 */
		@Override
		protected Connection initialValue() {
			Connection conn = null;
			
			try {
				conn = this.dataSource.getConnection();
			} catch (SQLException e) {
				throw new RuntimeException(e);
			}
			
			return conn;
		}
	}
	
}
